package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.repository;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import com.github.pagehelper.PageHelper;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.UserEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.StringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowBusi;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowUser;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.User;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.app.FlowApp;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.app.FlowAppAddVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.app.FlowAppBusiSimpleVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.app.FlowAppQueryVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.app.FlowAppUpdateVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowAppRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowBusiRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.InitCreatorRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.UserRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowAppInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowAppInfoExample;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowUserAppInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowUserAppInfoExample;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.FlowAppInfoDAO;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.FlowUserAppInfoDAO;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

/**
 * Service - 应用
 *
 * @author lilh
 * @date 2019-01-22 15:55
 */
@Repository
public class FlowAppRepositoryImpl implements FlowAppRepository {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private FlowAppInfoDAO flowAppInfoDAO;

    @Autowired
    private InitCreatorRepository initCreatorRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private FlowBusiRepository flowBusiRepository;

    @Autowired
    private FlowUserAppInfoDAO flowUserAppInfoDAO;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer insert(FlowAppAddVo flowAppAddVo) {
        FlowAppInfo entity = new FlowAppInfo();
        BeanUtils.copyProperties(flowAppAddVo, entity);
        initCreatorRepository.init(entity);
        flowAppInfoDAO.insertSelective(entity);

        //给创建人授权
        User current = userRepository.getCurrentUser();
        insertUserAppRelation(current, Objects.requireNonNull(convert(entity)), current.getId(), current.getUserCode());
        return entity.getId();
    }

    @Override
    public List<FlowApp> list(FlowAppQueryVo queryVo) {
        PageHelper.startPage(queryVo.getPageNum(), queryVo.getPageSize(), true);
        FlowAppInfoExample example = new FlowAppInfoExample();
        example.setOrderByClause("create_time desc");
        example.createCriteria();
        List<FlowAppInfo> flowAppInfos = flowAppInfoDAO.selectByExample(example);
        return flowAppInfos.stream().map(item -> {
            FlowApp app = new FlowApp();
            BeanUtils.copyProperties(item, app);
            return app;
        }).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer update(FlowAppUpdateVo flowAppUpdateVo) {
        FlowAppInfo exist = flowAppInfoDAO.selectByPrimaryKey(flowAppUpdateVo.getId());
        if (Objects.isNull(exist)) {
            logger.error("应用不存在{}", flowAppUpdateVo.getId());
            return 0;
        }
        // 判断appCode是否有修改
        if (StringUtils.isNotEmpty(flowAppUpdateVo.getAppCode()) && !Objects.equals(exist.getAppCode(), flowAppUpdateVo.getAppCode())) {
            List<FlowApp> appInfos = listByAppCode(flowAppUpdateVo.getAppCode());
            if (CollectionUtils.isNotEmpty(appInfos)) {
                return 0;
            }
        } else {
            flowAppUpdateVo.setAppCode(null);
        }
        FlowAppInfo entity = new FlowAppInfo();
        BeanUtils.copyProperties(flowAppUpdateVo, entity);
        entity.setUpdateTime(new Date());
        return flowAppInfoDAO.updateByPrimaryKeySelective(entity);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(Integer appId) {
        // 删除用户与应用的关联
        User user = userRepository.getCurrentUser();

        FlowUserAppInfoExample userAppInfoExample = new FlowUserAppInfoExample();
        userAppInfoExample.createCriteria().andUserIdEqualTo(user.getId()).andAppIdEqualTo(appId);
        flowUserAppInfoDAO.deleteByExample(userAppInfoExample);
        //todo 在什么条件下可以删除，应用下的业务怎么处理
        return flowAppInfoDAO.deleteByPrimaryKey(appId) > 0;
    }

    @Override
    public FlowApp detail(Integer appId) {
        return convert(flowAppInfoDAO.selectByPrimaryKey(appId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<FlowApp> listByAppId(Integer id) {
        User user = userRepository.getCurrentUser();
        FlowAppInfoExample example = new FlowAppInfoExample();
        example.setOrderByClause("create_time desc");
        // 判断用户是否超级管理员
        if (UserEnum.SUPER.type().equals(user.getUserType())) {
            if (Objects.nonNull(id)) {
                example.createCriteria().andIdEqualTo(id);
            }
            return flowAppInfoDAO.selectByExample(example).stream().map(this::convert).collect(Collectors.toList());
        }

        FlowUserAppInfoExample userAppInfoExample = new FlowUserAppInfoExample();
        userAppInfoExample.createCriteria().andUserIdEqualTo(user.getId());
        List<FlowUserAppInfo> flowUserAppInfos = flowUserAppInfoDAO.selectByExample(userAppInfoExample);
        List<String> appCodes = new ArrayList<>(flowUserAppInfos.size());
        for (FlowUserAppInfo flowUserAppInfo : flowUserAppInfos) {
            if (Objects.nonNull(id)) {
                if (flowUserAppInfo.getAppId().equals(id)) {
                    appCodes.add(flowUserAppInfo.getAppCode());
                }
            } else {
                appCodes.add(flowUserAppInfo.getAppCode());
            }
        }
        if (appCodes.isEmpty()) {
            return new ArrayList<>();
        }
        // TODO 保证用户所属租户与应用所属租户一致
        example.createCriteria()
                .andAppCodeIn(appCodes);
        return flowAppInfoDAO.selectByExample(example).stream().map(this::convert).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean verifyAppAuthorized(User user, Integer appId, Integer busiId) {
        boolean hasApp = Objects.nonNull(appId) && appId != 0;
        boolean hasBusi = Objects.nonNull(busiId) && busiId != 0;
        if (!hasApp && !hasBusi) {
            return false;
        }

        FlowUserAppInfoExample userAppInfoExample = new FlowUserAppInfoExample();
        userAppInfoExample.createCriteria().andUserIdEqualTo(user.getId());
        List<FlowUserAppInfo> userAppInfoList = flowUserAppInfoDAO.selectByExample(userAppInfoExample);
        List<Integer> appIds = new ArrayList<>(userAppInfoList.size());
        for (FlowUserAppInfo userAppInfo : userAppInfoList) {
            appIds.add(userAppInfo.getAppId());
        }
        if (hasApp) {
            if (!appIds.contains(appId)) {
                return false;
            }
        }
        if (hasBusi) {
            List<FlowBusi> busiInfoList;
            if (hasApp) {
                busiInfoList = flowBusiRepository.list(appId);
            } else {
                busiInfoList = flowBusiRepository.list(appIds);
            }
            for (FlowBusi busiInfo : busiInfoList) {
                if (busiId.equals(busiInfo.getId())) {
                    return true;
                }
            }
        }
        return !hasBusi;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer authorize(Integer appId, List<FlowUser> userList) {
        User current = userRepository.getCurrentUser();

        FlowApp appInfo = detail(appId);
        if (Objects.isNull(appInfo)) {
            return 0;
        }

        List<String> userCodes = userList.stream().map(FlowUser::getUserCode).collect(Collectors.toList());

        //1.根据appId和userCode去关联表查询
        List<FlowUserAppInfo> userApps = resolveUserApps(appId, userCodes);
        Set<String> existInAppUsers = userApps.stream().map(FlowUserAppInfo::getUserCode).collect(Collectors.toSet());
        List<User> users = userRepository.listUserByUserCodes(userCodes);
        Map<String, User> userCodeToUserMap = users.stream().collect(Collectors.toMap(User::getUserCode, item -> item, (t1, t2) -> t1));

        // 获取应用已授权用户
        int count = 0;
        for (FlowUser flowUserInfo : userList) {
            User user = userCodeToUserMap.get(flowUserInfo.getUserCode());
            if (Objects.isNull(user)) {
                //用户不存在
                logger.warn("用户{}-{}不存在", flowUserInfo.getUserCode(), flowUserInfo.getUserName());
                continue;
            }
            // 判断用户是否已授权
            if (!existInAppUsers.contains(flowUserInfo.getUserCode())) {
                // 添加应用与用户关联
                insertUserAppRelation(current, appInfo, user.getId(), user.getUserCode());
                count++;
            }
        }

        return count;
    }

    private List<FlowUserAppInfo> resolveUserApps(Integer appId, List<String> userCodes) {
        FlowUserAppInfoExample query = new FlowUserAppInfoExample();
        query.createCriteria().andAppIdEqualTo(appId).andUserCodeIn(userCodes);
        return flowUserAppInfoDAO.selectByExample(query);
    }

    private void insertUserAppRelation(User current, FlowApp appInfo, Integer userId, String userCode) {
        FlowUserAppInfo userAppInfo = new FlowUserAppInfo();
        userAppInfo.setAppId(appInfo.getId());
        userAppInfo.setAppCode(appInfo.getAppCode());
        userAppInfo.setUserId(userId);
        userAppInfo.setUserCode(userCode);
        userAppInfo.setCreatorId(current.getId());
        userAppInfo.setCreatorName(current.getUserName());
        userAppInfo.setCreateTime(new Date());
        userAppInfo.setTenantId(appInfo.getTenantId());
        flowUserAppInfoDAO.insertSelective(userAppInfo);
    }

    @Override
    public List<FlowAppBusiSimpleVo> getAppAndBusiList(List<FlowApp> flowAppInfos) {
        List<FlowAppBusiSimpleVo> apps = new ArrayList<>(flowAppInfos.size());

        for (FlowApp flowAppInfo : flowAppInfos) {
            FlowAppBusiSimpleVo appSimpleVo = new FlowAppBusiSimpleVo();
            appSimpleVo.setId(flowAppInfo.getId());
            appSimpleVo.setName(flowAppInfo.getAppName());

            List<FlowBusi> busiInfos = flowBusiRepository.list(flowAppInfo.getId());
            List<Object> busiList = new ArrayList<>(busiInfos.size());
            for (FlowBusi busiInfo : busiInfos) {
                FlowAppBusiSimpleVo busiSimpleVo = new FlowAppBusiSimpleVo();
                busiSimpleVo.setId(busiInfo.getId());
                busiSimpleVo.setName(busiInfo.getBusiName());
                busiList.add(busiSimpleVo);
            }
            appSimpleVo.setBusiness(busiList);
            apps.add(appSimpleVo);
        }
        return apps;
    }

    @Override
    public List<FlowApp> listByAppCode(String appCode) {
        FlowAppInfoExample appInfoExample = new FlowAppInfoExample();
        appInfoExample.createCriteria().andAppCodeEqualTo(appCode);
        return list(appInfoExample);
    }

    @Override
    public List<FlowApp> listByTenantId(Integer tenantId) {
        FlowAppInfoExample flowAppInfoExample = new FlowAppInfoExample();
        flowAppInfoExample.createCriteria().andTenantIdEqualTo(tenantId);
        return list(flowAppInfoExample);
    }

    @Override
    public List<FlowApp> listByTenantIds(List<Integer> tenantIds) {
        FlowAppInfoExample query = new FlowAppInfoExample();
        query.createCriteria().andTenantIdIn(tenantIds);
        return list(query);
    }

    private List<FlowApp> list(FlowAppInfoExample appInfoExample) {
        return flowAppInfoDAO.selectByExample(appInfoExample).stream().map(this::convert).collect(Collectors.toList());
    }

    @Override
    public Integer authorizedCancel(Integer appId, List<FlowUser> userList) {

        FlowApp appInfo = detail(appId);
        if (Objects.isNull(appInfo)) {
            return 0;
        }

        int count = 0;
        for (FlowUser flowUserInfo : userList) {
            FlowUserAppInfoExample userAppInfoExample = new FlowUserAppInfoExample();
            userAppInfoExample.createCriteria().andUserIdEqualTo(flowUserInfo.getId()).andAppIdEqualTo(appId);
            int delete = flowUserAppInfoDAO.deleteByExample(userAppInfoExample);
            if (delete > 0) {
                count++;
            }
        }

        return count;
    }

    @Override
    public boolean authorizedCancel(List<Integer> userIds, Integer tenantId) {
        if (CollectionUtils.isEmpty(userIds)) {
            return false;
        }
        FlowUserAppInfoExample query = new FlowUserAppInfoExample();
        query.createCriteria().andTenantIdEqualTo(tenantId)
                .andUserIdIn(userIds);
        flowUserAppInfoDAO.deleteByExample(query);
        return true;
    }

    @Override
    public List<FlowApp> listAppByTenantIdAndUserCode(Integer tenantId, String userCode) {
        return listApps(Collections.singletonList(tenantId), userCode);
    }

    @Override
    public List<FlowApp> listApps(List<Integer> tenantIds, String userCode) {
        return listApps(tenantIds, Collections.singletonList(userCode));
    }

    @Override
    public List<FlowApp> getAppInfoByProcessInstanceId(String processInstanceId) {
        List<FlowAppInfo> flowAppInfos = flowAppInfoDAO.getAppInfoByProcessInstanceId(processInstanceId);
        if(CollectionUtils.isEmpty(flowAppInfos)) {
            return Collections.emptyList();
        }
        return flowAppInfos.stream().map(this::convert).collect(Collectors.toList());
    }

    private List<FlowApp> listApps(List<Integer> tenantIds, List<String> userCodes) {
        FlowUserAppInfoExample query = new FlowUserAppInfoExample();
        query.createCriteria().andTenantIdIn(tenantIds).andUserCodeIn(userCodes);
        List<FlowUserAppInfo> userApps = flowUserAppInfoDAO.selectByExample(query);
        if (CollectionUtils.isEmpty(userApps)) {
            return Collections.emptyList();
        }
        FlowAppInfoExample appQuery = new FlowAppInfoExample();
        appQuery.createCriteria().andIdIn(userApps.stream().map(FlowUserAppInfo::getAppId).collect(Collectors.toList()))
                .andTenantIdIn(tenantIds);
        return flowAppInfoDAO.selectByExample(appQuery).stream().map(this::convert).collect(Collectors.toList());
    }


    private FlowApp convert(FlowAppInfo info) {
        if (Objects.isNull(info)) {
            return null;
        }
        FlowApp app = new FlowApp();
        BeanUtils.copyProperties(info, app);
        return app;
    }


}
